﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Host.cs" company="">
//   
// </copyright>
// <summary>
//   服务寄主
// </summary>
// --------------------------------------------------------------------------------------------------------------------

#region

using System;
using System.Collections;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Web;
using IveelySE.Common;
using IveelySE.Common.Log;
using IveelySE.Index;

#endregion

namespace IveelySE.InternetService
{
    /// <summary>
    ///     服务寄主
    /// </summary>
    public class Host : ITask
    {
        /// <summary>
        ///     监听器
        /// </summary>
        private static TcpListener _listener;

        /// <summary>
        ///     媒体类型
        /// </summary>
        private static Hashtable _mimeTypes;

        /// <summary>
        ///     端口号
        /// </summary>
        private static int port = 8088;

        /// <summary>
        /// Initializes a new instance of the <see cref="Host"/> class.
        /// </summary>
        public Host()
        {
            Name = " [网络信息服务] ";
        }

        /// <summary>
        ///     按照默认设置
        /// </summary>
        public static void InitDefault()
        {
            if(_listener == null)
            {
                SetMimeTypes();
                _listener = new TcpListener(port);
                _listener.Start();
            }

            // 搜索服务
            Search.Init();
        }


        /// <summary>
        /// The sumbit.
        /// </summary>
        /// <param name="parameters">
        /// The parameters.
        /// </param>
        public override void Sumbit(object parameters)
        {
            InitDefault();
            string rootDir = Environment.CurrentDirectory + "\\IveelyWeb";
            while(true)
            {
                Socket socket = _listener.AcceptSocket();
                if(socket.Connected)
                {
                    var receive = new byte[1024];
                    socket.Receive(receive, receive.Length, 0);
                    string sBuffer = Encoding.ASCII.GetString(receive);
                    if(sBuffer.Substring(0, 3) != "GET")
                    {
                        socket.Close();
                        return;
                    }

                    int startPos = sBuffer.IndexOf("HTTP", 1);
                    string httpVersion = sBuffer.Substring(startPos, 8);

                    string request = sBuffer.Substring(0, startPos - 1);


                    request.Replace("\\", "/");
                    if((request.IndexOf(".") < 1) && (!request.EndsWith("/")))
                    {
                        request = request + "/";
                    }

                    startPos = request.LastIndexOf("/") + 1;
                    string query;
                    string requestedFile = GetFileName(request.Substring(startPos), out query);
                    string dirName = request.Substring(request.IndexOf("/"), request.LastIndexOf("/") - 3);
                    string localDir;
                    if(dirName == "/")
                        localDir = rootDir + "\\";
                    else
                    {
                        localDir = rootDir + dirName.Replace('/', '\\');
                    }

                    string errorMessage;
                    if(localDir.Length == 0)
                    {
                        errorMessage = "<H2>Error!! Requested Directory does not exists</H2><Br>";
                        SendHeader(httpVersion, string.Empty, errorMessage.Length, " 404 Not Found", ref socket);
                        SendToBrowser(errorMessage, ref socket);
                        socket.Close();
                        continue;
                    }

                    if(requestedFile.Length == 0)
                    {
                        requestedFile = "Default.html";

                        if(requestedFile == string.Empty)
                        {
                            errorMessage = "<H2>Error!! No Default File Name Specified</H2>";
                            SendHeader(httpVersion, string.Empty, errorMessage.Length, " 404 Not Found", ref socket);
                            SendToBrowser(errorMessage, ref socket);

                            socket.Close();

                            return;
                        }
                    }

                    string mimeType = GetMimeType(requestedFile);
                    string physicalFilePath = localDir + requestedFile;


                    if(File.Exists(physicalFilePath) == false)
                    {
                        errorMessage = "<H2>404 Error! File Does Not Exists...</H2>";
                        SendHeader(httpVersion, string.Empty, errorMessage.Length, " 404 Not Found", ref socket);
                        SendToBrowser(errorMessage, ref socket);
                    }
                    else
                    {
                        int iTotBytes = 0;
                        if(query == string.Empty)
                        {
                            var fs = new FileStream(physicalFilePath, FileMode.Open, FileAccess.Read, 
                                                    FileShare.Read);

                            var reader = new BinaryReader(fs);
                            var bytes = new byte[fs.Length];
                            int read;
                            while((read = reader.Read(bytes, 0, bytes.Length)) != 0)
                            {
                                iTotBytes = iTotBytes + read;
                            }

                            reader.Close();
                            fs.Close();
                            SendHeader(httpVersion, mimeType, iTotBytes, " 200 OK", ref socket);
                            SendToBrowser(bytes, ref socket);
                        }
                        else
                        {
                            string content = File.ReadAllText(physicalFilePath, Encoding.UTF8);
                            content = content.Replace("@@Result@@", GetResult(query));
                            var stream = new MemoryStream(Encoding.UTF8.GetBytes(content));
                            var reader = new BinaryReader(stream);
                            var bytes = new byte[stream.Length];
                            int read;
                            while((read = reader.Read(bytes, 0, bytes.Length)) != 0)
                            {
                                iTotBytes = iTotBytes + read;
                            }

                            reader.Close();
                            stream.Close();
                            SendHeader(httpVersion, mimeType, iTotBytes, " 200 OK", ref socket);
                            SendToBrowser(bytes, ref socket);
                            query = string.Empty;
                        }
                    }

                    socket.Close();
                }
            }
        }

        /// <summary>
        /// The get result.
        /// </summary>
        /// <param name="request">
        /// The request.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        private static string GetResult(string request)
        {
            try
            {
                // 执行搜索过程
                string result = Search.GetResult(HttpUtility.UrlDecode(request));
                return result.Length == 0 ? "未找到符合条件的结果." : result;
            }
            catch(Exception exception)
            {
                return exception.ToString();
            }
        }

        /// <summary>
        /// 获取媒体类型
        /// </summary>
        /// <param name="requestFile">
        /// 请求的文件
        /// </param>
        /// <returns>
        /// 返回媒体类型
        /// </returns>
        public string GetMimeType(string requestFile)
        {
            string fileExtend = requestFile.ToLower().Substring(requestFile.IndexOf(".", StringComparison.Ordinal));
            if(_mimeTypes.ContainsKey(fileExtend))
            {
                return _mimeTypes[fileExtend].ToString();
            }

            return string.Empty;
        }

        /// <summary>
        /// 返回头部信息
        /// </summary>
        /// <param name="httpVersion">
        /// HTTP 版本
        /// </param>
        /// <param name="mimeHeader">
        /// 媒体类型
        /// </param>
        /// <param name="totalBytes">
        /// body长度
        /// </param>
        /// <param name="statusCode">
        /// 状态.
        /// </param>
        /// <param name="socket">
        /// Socket
        /// </param>
        public void SendHeader(string httpVersion, string mimeHeader, int totalBytes, string statusCode, 
                               ref Socket socket)
        {
            string buffer = string.Empty;

            // 如果没有媒体类型
            if(mimeHeader.Length == 0)
            {
                // 默认设定为html
                mimeHeader = "text/html";
            }

            buffer += httpVersion + statusCode + "\r\n";
            buffer += "Server: cx1193719-b\r\n";
            buffer += "Content-Type: " + mimeHeader + "\r\n";
            buffer += "Accept-Ranges: bytes\r\n";
            buffer += "Content-Length: " + totalBytes + "\r\n\r\n";

            byte[] sendData = Encoding.ASCII.GetBytes(buffer);

            SendToBrowser(sendData, ref socket);

        }

        /// <summary>
        /// The send to browser.
        /// </summary>
        /// <param name="sData">
        /// The s data.
        /// </param>
        /// <param name="mySocket">
        /// The my socket.
        /// </param>
        public void SendToBrowser(string sData, ref Socket mySocket)
        {
            SendToBrowser(Encoding.ASCII.GetBytes(sData), ref mySocket);
        }


        /// <summary>
        /// 发送信息到浏览端
        /// </summary>
        /// <param name="sendData">
        /// 字节数组
        /// </param>
        /// <param name="socket">
        /// Socket reference
        /// </param>
        public void SendToBrowser(byte[] sendData, ref Socket socket)
        {
            try
            {
                if(socket.Connected)
                {
                    socket.Send(sendData, sendData.Length, 0);
                }
            }
            catch(Exception e)
            {
                IveelySE.Common.Log.Content.GetInstance().Write(e,MessageType.Warning);
            }
        }

        /// <summary>
        /// The get file name.
        /// </summary>
        /// <param name="requstFile">
        /// The requst file.
        /// </param>
        /// <param name="query">
        /// The query.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        private string GetFileName(string requstFile, out string query)
        {
            if(requstFile.Contains("?"))
            {
                // example: result.html?query=a
                query = requstFile.Substring(requstFile.LastIndexOf("=") + 1);
                return requstFile.Substring(0, requstFile.IndexOf("?"));
            }

            query = string.Empty;
            return requstFile;
        }

        /// <summary>
        ///     初始化设置支持的媒体类型
        /// </summary>
        private static void SetMimeTypes()
        {
            string[] lines = File.ReadAllLines(@"mime.dat");
            _mimeTypes = new Hashtable();
            foreach(string line in lines)
            {
                string[] mime = line.Split('\t');
                if(mime.Length == 2 && !_mimeTypes.ContainsKey(mime[0]))
                {
                    _mimeTypes.Add(mime[0].ToLower(), mime[1].ToLower());
                }
            }
        }
    }
}